package com.zhipin.hackthon;

import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Level;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Param;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.runner.Runner;
import org.openjdk.jmh.runner.RunnerException;
import org.openjdk.jmh.runner.options.Options;
import org.openjdk.jmh.runner.options.OptionsBuilder;

import java.util.concurrent.ThreadLocalRandom;
import java.util.concurrent.TimeUnit;

import static java.lang.Integer.MAX_VALUE;

/**
 * 为了准确的评估不同数据结构的性能，使用 <a href="https://github.com/openjdk/jmh">JMH</a> 进行吞吐量评估.
 * <p>
 * 测试流程：
 * <blockquote>
 * <pre>
 * 1、把 {@link MultiAtomicIntegerBenchmark#yourMultiAtomicInteger} 替换为您自己实现的 MultiAtomicInteger；
 * 2、依次修改 Java 程序的启动参数（Program arguments）为以下组合，并执行 {@link MultiAtomicIntegerBenchmark#main(String[])} 方法，
 * 并把运行结果截图保留：
 * a、1 1 3 5 （仅供参考，不作为最终评判依据）
 * b、1 2 3 5 （仅供参考，不作为最终评判依据）
 * c、1 4 3 5 （作为最终评判依据）
 * d、1 8 3 5 （仅供参考，不作为最终评判依据）
 * </pre>
 * </blockquote>
 * 说明：该类的 main 方法的启动参数含义为：${forks} ${threads} ${warmupIterations} ${measurementIterations}
 */
@BenchmarkMode({Mode.Throughput})
@OutputTimeUnit(TimeUnit.MICROSECONDS)
@State(Scope.Benchmark)
public class MultiAtomicIntegerBenchmark {

    private MultiAtomicInteger stdMultiAtomicInteger;

    private MultiAtomicInteger yourMultiAtomicInteger;

    @Param(value = {"1048576"}) //1024 * 1024
    private int testSize;

    @Setup(Level.Iteration)
    public void setup() {
        stdMultiAtomicInteger = new StandardMultiAtomicInteger(testSize);
        yourMultiAtomicInteger = new ZslMultiAtomicInteger(testSize); //TODO: 替换为您自己实现的 MultiAtomicInteger

        final ThreadLocalRandom random = ThreadLocalRandom.current();
        for (int i = 0; i < testSize; i++) {
            final int key = random.nextInt(0, MAX_VALUE) & (testSize - 1);
            stdMultiAtomicInteger.addAndGet(key, 1);
            yourMultiAtomicInteger.addAndGet(key, 1);
        }
    }

    @Benchmark
    public int stdMultiAtomicInteger(ThreadState state) {
        return stdMultiAtomicInteger.addAndGet(randomKey(state), 1);
    }

    private int randomKey(ThreadState state) {
        return state.next() & (testSize - 1);
    }

    @Benchmark
    public int yourMultiAtomicInteger(ThreadState state) {
        return yourMultiAtomicInteger.addAndGet(randomKey(state), 1);
    }

    public static void main(String[] args) throws RunnerException {
        if (args.length < 4) {
            System.err.println("java -jar xxx.jar ${forks} ${threads} ${warmupIterations} ${measurementIterations}");
            return;
        }

        final Options opt = new OptionsBuilder()
                .include(MultiAtomicIntegerBenchmark.class.getSimpleName())
                .forks(Integer.parseInt(args[0]))
                .threads(Integer.parseInt(args[1]))
                .warmupIterations(Integer.parseInt(args[2]))
                .measurementIterations(Integer.parseInt(args[3]))
                .build();
        new Runner(opt).run();
    }

    @State(Scope.Thread)
    public static class ThreadState {

        private final SimpleRandom random = new SimpleRandom();

        int next() {
            return random.next();
        }
    }
}
